package ru.usu.gv.utils.exports;

import com.sun.image.codec.jpeg.JPEGCodec;
import com.sun.image.codec.jpeg.JPEGEncodeParam;
import com.sun.image.codec.jpeg.JPEGImageEncoder;

import javax.imageio.ImageIO;
import javax.media.j3d.Alpha;
import javax.media.j3d.Canvas3D;
import javax.media.j3d.ImageComponent;
import javax.media.j3d.ImageComponent2D;

import java.awt.GraphicsConfiguration;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;

/**
 * An offscreen canvas with a print method that writes out a
 * screen capture image to a jpg file
 */
class OffScreenCanvas3D extends Canvas3D
{
	/**
	 * You should create the canvas and attach to your view when
	 * you set that view up 
	 * @param gconfig use the same graphics config as your universe
	 */
	public OffScreenCanvas3D(GraphicsConfiguration gconfig)
	{
		super(gconfig, true);
	}

	BufferedImage doRender(int width, int height)
	{

		BufferedImage bImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);

		ImageComponent2D buffer = new ImageComponent2D(ImageComponent.FORMAT_RGB, bImage);

		setOffScreenBuffer(buffer);
		renderOffScreenBuffer();
		waitForOffScreenRendering();
		bImage = getOffScreenBuffer().getImage();

		// To release the reference of buffer inside Java 3D.
		setOffScreenBuffer(null);

		return bImage;
	}

	public void saveToJPEG(String path)
	{
		// write that to disk....
		if (!path.endsWith(".jpg"))
		{
			path = path + ".jpg";
		}
		try
		{
			//BufferedImage bImage = doRender(getScreen3D().getSize().width, getScreen3D().getSize().height);
			BufferedImage bImage = doRender(1024, 768);
			FileOutputStream out = new FileOutputStream(path);

			JPEGImageEncoder encoder = JPEGCodec.createJPEGEncoder(out);
			JPEGEncodeParam param = encoder.getDefaultJPEGEncodeParam(bImage);
			param.setQuality(1.0f, false); // 100% qualith JPEG
			encoder.setJPEGEncodeParam(param);
			encoder.encode(bImage);
			out.close();
			System.out.println("Wrote File: " + path);
		}
		catch (IOException e)
		{
			System.out.println("I/O exception!");
		}
	}

	public void saveToPNG(String path)
	{
		// write that to disk....
		if (!path.endsWith(".png"))
		{
			path = path + ".png";
		}
		try
		{
			BufferedImage bImage = doRender(getScreen3D().getSize().width, getScreen3D().getSize().height);
			FileOutputStream out = new FileOutputStream(path);
			if (!ImageIO.write(bImage, "png", out))
				System.out.println("Error during saving file '" + path + "'");
			else
				System.out.println("Wrote File: " + path);
			out.close();
		}
		catch (IOException e)
		{
			System.out.println("I/O exception!");
		}
	}

	public void saveToBMP(String path)
	{
		// write that to disk....
		if (!path.endsWith(".bmp"))
		{
			path = path + ".bmp";
		}
		try
		{
			BufferedImage bImage = doRender(getScreen3D().getSize().width, getScreen3D().getSize().height);
			FileOutputStream out = new FileOutputStream(path);
			if (!ImageIO.write(bImage, "BMP", out))
				System.out.println("Error during saving file '" + path + "'");
			else
				System.out.println("Wrote File: " + path);
			out.close();
		}
		catch (IOException e)
		{
			System.out.println("I/O exception!");
		}
	}

	public void print()
	{
		BufferedImage bImage = doRender(getScreen3D().getSize().width, getScreen3D().getSize().height);
		new ImagePrinter(bImage).print();
	}

	public void saveToAVI(String path)
	{
		// write that to disk....
		if (!path.endsWith(".avi"))
		{
			path = path + ".avi";
		}
		try
		{
			//MovieTool.generateVideo(this, 5000, new Alpha(), new File(path), 60, getScreen3D().getSize().width, getScreen3D().getSize().height);
			MovieTool.generateVideo(this, 10000, new Alpha(), new File(path), 15, 1024, 768);

			System.out.println("Successfully created video: " + path);
		}
		catch (Exception e)
		{
			System.out.println("Exception!");
			e.printStackTrace();
		}
	}

	@Override
	public void postSwap()
	{
		// No-op since we always wait for off-screen rendering to complete
	}
}
